package IC.Parser;

import java_cup.runtime.Symbol;

%%

%class Lexer
%public
%function next_token
%type Token
%line 
%scanerror LexicalError
%cup

%state COMMENT_SINGLE
%state COMMENT_MULTI
%state COMMENT_MULTI_ASTERIX
%state STRING_STATE

%{
	StringBuilder m_String = new StringBuilder();
	
	public int getLineNumber()
	{
		return yyline;
	}
%}

%eofval{
	if (yystate() == COMMENT_MULTI || yystate() == COMMENT_MULTI_ASTERIX) 
		throw new LexicalError("Lexical error: Comment left open", yyline);
	else if (yystate() == STRING_STATE) 
		throw new LexicalError("Lexical error: String left open", yyline);
	else
  		return new Token(sym.EOF, "EOF", yyline);
%eofval}

ALPHA=[A-Z|a-z] 
UPPERCASE = [A-Z]
LOWERCASE = [a-z]
DIGIT=[0-9]
ALPHA_NUMERIC={ALPHA}|{DIGIT}
WHITE_SPACE = [ |\r|\n|\r\n|\t]
%%

<YYINITIAL> {WHITE_SPACE} { yybegin(YYINITIAL); }


<YYINITIAL> "//" { yybegin(COMMENT_SINGLE); }
<YYINITIAL> "/*" { yybegin(COMMENT_MULTI); }

<COMMENT_SINGLE> [^\n] {}  						// Ignore all but new-line
<COMMENT_SINGLE> [\n] { yybegin(YYINITIAL); } 	// New-line ends single-line comment

<COMMENT_MULTI> "*" { yybegin(COMMENT_MULTI_ASTERIX); }
<COMMENT_MULTI> [^*] { }

<COMMENT_MULTI_ASTERIX> "/" { yybegin(YYINITIAL); }
<COMMENT_MULTI_ASTERIX> "*" { }
<COMMENT_MULTI_ASTERIX> [^*/] { yybegin(COMMENT_MULTI); }


<YYINITIAL> "[" { return new Token(sym.LB, "LB", yyline); }
<YYINITIAL> "]" { return new Token(sym.RB, "RB", yyline); }
<YYINITIAL> "(" { return new Token(sym.LP, "LP", yyline); }
<YYINITIAL> ")" { return new Token(sym.RP, "RP", yyline); }
<YYINITIAL> "{" { return new Token(sym.LCBR, "LCBR", yyline); }
<YYINITIAL> "}" { return new Token(sym.RCBR, "RCBR", yyline); }
<YYINITIAL> "-" { return new Token(sym.MINUS, "MINUS", yyline); }
<YYINITIAL> "*" { return new Token(sym.MULTIPLY, "MULTIPLY", yyline); }
<YYINITIAL> "/" { return new Token(sym.DIVIDE, "DIVIDE", yyline); }
<YYINITIAL> "%" { return new Token(sym.MOD, "MOD", yyline); }
<YYINITIAL> "+" { return new Token(sym.PLUS, "PLUS", yyline); }
<YYINITIAL> "<=" { return new Token(sym.LTE, "LTE", yyline); }
<YYINITIAL> "<" { return new Token(sym.LT, "LT", yyline); }
<YYINITIAL> ">=" { return new Token(sym.GTE, "GTE", yyline); }
<YYINITIAL> ">" { return new Token(sym.GT, "GT", yyline); }
<YYINITIAL> "==" { return new Token(sym.EQUAL, "EQUAL", yyline); }
<YYINITIAL> "=" { return new Token(sym.ASSIGN, "ASSIGN", yyline); }
<YYINITIAL> "!" { return new Token(sym.LNEG, "LNEG", yyline); }
<YYINITIAL> "!=" { return new Token(sym.NEQUAL, "NEQUAL", yyline); }
<YYINITIAL> "&&" { return new Token(sym.LAND, "LAND", yyline); }
<YYINITIAL> "||" { return new Token(sym.LOR, "LOR", yyline); }
<YYINITIAL> "," { return new Token(sym.COMMA, "COMMA", yyline); }
<YYINITIAL> "." { return new Token(sym.DOT, "DOT", yyline); }
<YYINITIAL> ";" { return new Token(sym.SEMI, "SEMI", yyline); }

<YYINITIAL> "class" { return new Token(sym.CLASS, "CLASS", yyline); }
<YYINITIAL> "extends" { return new Token(sym.EXTENDS, "EXTENDS", yyline); }
<YYINITIAL> "static" { return new Token(sym.STATIC, "STATIC", yyline); }
<YYINITIAL> "void" { return new Token(sym.VOID, "VOID", yyline); }
<YYINITIAL> "int" { return new Token(sym.INT, "INT", yyline); }
<YYINITIAL> "boolean" { return new Token(sym.BOOLEAN, "BOOLEAN",  yyline); }
<YYINITIAL> "string" { return new Token(sym.STRING, "STRING", yyline); }
<YYINITIAL> "return" { return new Token(sym.RETURN, "RETURN", yyline); }
<YYINITIAL> "if" { return new Token(sym.IF, "IF", yyline); }
<YYINITIAL> "else" { return new Token(sym.ELSE, "ELSE", yyline); }
<YYINITIAL> "while" { return new Token(sym.WHILE, "WHILE", yyline); }
<YYINITIAL> "break" { return new Token(sym.BREAK, "BREAK", yyline); }
<YYINITIAL> "continue" { return new Token(sym.CONTINUE, "CONTINUE", yyline); }
<YYINITIAL> "this" { return new Token(sym.THIS, "THIS", yyline); }
<YYINITIAL> "new" { return new Token(sym.NEW, "NEW", yyline); }
<YYINITIAL> "length" { return new Token(sym.LENGTH, "LENGTH", yyline); }
<YYINITIAL> "true" { return new Token(sym.TRUE, "TRUE", yyline); }
<YYINITIAL> "false" { return new Token(sym.FALSE, "FALSE", yyline); }
<YYINITIAL> "null" { return new Token(sym.NULL, "NULL", yyline); }

<YYINITIAL> \" { 
		m_String.setLength(0); 
		yybegin(STRING_STATE); 
		}
<STRING_STATE> {
	\" { 
		yybegin(YYINITIAL); 
		return new Token(sym.QUOTE, "QUOTE", yyline, "\"" + m_String.toString() + "\""); 
		}
	([ |!|#-\[|\]-~]|"\\\\"|"\\\""|"\\t"|"\\n")* { m_String.append(yytext()); }
}


<YYINITIAL> ([0]+)|([1-9]({DIGIT})*) { 
	if (Long.valueOf(yytext()) >= Math.pow(2, 31))
		throw new LexicalError("Lexical error: integer out of range '" + yytext() + "'", yyline);
	else
		return new Token(sym.INTEGER, "INTEGER", yyline, yytext()); 
}

<YYINITIAL> (({DIGIT})+)({ALPHA_NUMERIC})+ { throw new LexicalError("Lexical error: identifier cannot start with a number '" + yytext() + "'", yyline);}

<YYINITIAL>  {UPPERCASE}({ALPHA_NUMERIC}|[_])* { return new Token(sym.CLASS_ID, "CLASS_ID", yyline, yytext()); }
<YYINITIAL>  {LOWERCASE}({ALPHA_NUMERIC}|[_])* { return new Token(sym.ID, "ID", yyline, yytext()); }



<YYINITIAL> . { throw new LexicalError("Lexical error: illegal character '" + yytext() + "'", yyline); }
